////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   BrushFace.cpp
//  Version:     v1.00
//  Created:     8/7/2002 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//  02/03/2010 Refactored by Jaesik.
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "Brush.h"
#include "BrushFace.h"

#define VER_EPS 0.01f

static Vec3 s_baseAxis[] =
{
	Vec3(0,0,1),   Vec3(1,0,0),  Vec3(0,-1,0),  // floor
	Vec3(0,0,-1),  Vec3(-1,0,0), Vec3(0,1,0),   // ceiling
	Vec3(1,0,0),   Vec3(0,1,0),  Vec3(0,0,-1),  // west wall
	Vec3(-1,0,0),  Vec3(0,-1,0), Vec3(0,0,-1),  // east wall
	Vec3(0,1,0),   Vec3(-1,0,0), Vec3(0,0,-1),	// south wall
	Vec3(0,-1,0),  Vec3(1,0,0),  Vec3(0,0,-1),  // north wall
};


void SBrushFace::MakePlane( const std::vector<SBrushVertex*>& vtxlist, int triIndex0, int triIndex1, int triIndex2 )
{
	this->plane = SBrushPlane(	vtxlist[pointindexlist[triIndex0]]->pos, 
															vtxlist[pointindexlist[triIndex1]]->pos, 
															vtxlist[pointindexlist[triIndex2]]->pos );
}


bool SBrushTriangle::SetVertexIndices(	const std::vector<SBrushVertex*>& vtxlist, 
																				SBrushCommon::VertexFaceIndexType i0, 
																				SBrushCommon::VertexFaceIndexType i1, 
																				SBrushCommon::VertexFaceIndexType i2, 
																				SBrushCommon::VertexFaceIndexType faceindex )
{
	assert(		i0 < (SBrushCommon::VertexFaceIndexType)vtxlist.size() && 
						i1 < (SBrushCommon::VertexFaceIndexType)vtxlist.size() &&  
						i2 < (SBrushCommon::VertexFaceIndexType)vtxlist.size() );

	Vec3 d0 = (vtxlist[i2]->pos-vtxlist[i1]->pos).GetNormalized();
	Vec3 d1 = (vtxlist[i0]->pos-vtxlist[i1]->pos).GetNormalized();

	if( d0.Dot(d1) > 0.99999f )
		return false;

	vertexindices[0] = i0;
	vertexindices[1] = i1;
	vertexindices[2] = i2;

	normal = d0^d1;
	normal.NormalizeSafe();
	CalcBound(vtxlist);
	faceidx = faceindex;

	return true;
}


bool SBrushTriangle::SetVertexIndices(	const Vec3& v0, const Vec3& v1, const Vec3& v2, 
																				SBrushCommon::VertexFaceIndexType i0, 
																				SBrushCommon::VertexFaceIndexType i1, 
																				SBrushCommon::VertexFaceIndexType i2, 
																				SBrushCommon::VertexFaceIndexType fidx )
{	
	Vec3 d0 = (v2-v1).GetNormalized();
	Vec3 d1 = (v1-v0).GetNormalized();

	if( d0.Dot(d1) > 0.99999f )
		return false;

	vertexindices[0] = i0;
	vertexindices[1] = i1;
	vertexindices[2] = i2;
	normal = (v2-v1)^(v0-v1);
	normal.NormalizeSafe();
	CalcBound(v0,v1,v2);
	faceidx = fidx;

	return true;
}


void SBrushTriangle::CalcBound( const std::vector<SBrushVertex*>& vtxlist )
{
	boundbox.Reset();

	for( int i = 0; i < 3; i++ )
	{
		SBrushCommon::VertexFaceIndexType index = vertexindices[i];
		for( int k = 0; k < 3; k++ )
		{
			if( boundbox.min[k] > vtxlist[index]->pos[k] )
				boundbox.min[k] = vtxlist[index]->pos[k];

			if( boundbox.max[k] < vtxlist[index]->pos[k] )
				boundbox.max[k] = vtxlist[index]->pos[k];
		}
	}
}


void SBrushTriangle::CalcBound( const Vec3& v0, const Vec3& v1, const Vec3& v2 )
{
	boundbox.Reset();

	const Vec3* v[3] = { &v0, &v1, &v2 };

	for( int i = 0; i < 3; i++ )
	{
		for( int k = 0; k < 3; k++ )
		{
			if( boundbox.min[k] > (*v[i])[k] )
				boundbox.min[k] = (*v[i])[k];

			if( boundbox.max[k] < (*v[i])[k] )
				boundbox.max[k] = (*v[i])[k];
		}
	}
}



void SBrushFace::CalcTexCoords( SBrushVertex& v ) 
{
	CalcTexCoords( plane, texinfo, v.pos, v.st[0], v.st[1] );
}


void SBrushFace::CalcTextureBasis( const SBrushPlane& p, const SBrushCommon::STexInfo& ti, Vec3& u, Vec3& v )
{
	Vec3  pvecs[2];
	float sinv, cosv;

	u.Set(0,0,0);
	v.Set(0,0,0);

	p.CalcTextureAxis(pvecs[0], pvecs[1]);

	u = pvecs[0];
	v = pvecs[1];

	if (ti.rotate == 0)
	{
		sinv = 0 ; cosv = 1;
	}
	else if (ti.rotate == 90)
	{
		sinv = 1 ; cosv = 0;
	}
	else if (ti.rotate == 180)
	{
		sinv = 0 ; cosv = -1;
	}
	else if (ti.rotate == 270)
	{
		sinv = -1 ; cosv = 0;
	}
	else
	{ 
		float ang = ti.rotate / 180 * gf_PI;
		sinv = sin(ang);
		cosv = cos(ang);
	}

	int sv,tv;
	if (pvecs[0].x)
		sv = 0;
	else if (pvecs[0].y)
		sv = 1;
	else
		sv = 2;

	if (pvecs[1].x)
		tv = 0;
	else if (pvecs[1].y)
		tv = 1;
	else
		tv = 2;

	for (int i=0 ; i<2 ; i++)
	{
		float ns = cosv * pvecs[i][sv] - sinv * pvecs[i][tv];
		float nt = sinv * pvecs[i][sv] + cosv * pvecs[i][tv];
		if (!i)
		{
			u[sv] = ns;
			u[tv] = nt;
		}
		else
		{
			v[sv] = ns;
			v[tv] = nt;
		}
	}
}


void SBrushFace::CalcTexCoords( const SBrushPlane& p, const SBrushCommon::STexInfo& ti, const Vec3& pos, float& tu, float &tv )
{
	Vec3 tVecx, tVecy;
	CalcTextureBasis( p, ti, tVecx, tVecy );
	tu = pos.Dot(tVecx)/ti.scale[0] + ti.shift[0];
	tv = pos.Dot(tVecy)/ti.scale[1] + ti.shift[1];
}


void SBrushFace::CalcBound( const std::vector<SBrushVertex*>& vtxlist, Vec3& mins, Vec3& maxs )
{
	CalcBound(vtxlist);
	mins = boundbox.min;
	maxs = boundbox.max;
}


void SBrushFace::CalcBound( const std::vector<SBrushVertex*>& vtxlist )
{
	boundbox.Reset();
	for( int i = 0; i < pointindexlist.size(); i++ )
	{
		for( int k = 0; k < 3; k++ )
		{
			if( vtxlist[pointindexlist[i]]->pos[k] < boundbox.min[k] )
				boundbox.min[k] = vtxlist[pointindexlist[i]]->pos[k];

			if( vtxlist[pointindexlist[i]]->pos[k] > boundbox.max[k] )
				boundbox.max[k] = vtxlist[pointindexlist[i]]->pos[k];
		}
	}
}



void SBrushFace::CalcCenter( const std::vector<SBrushVertex*>& vtxlist )
{
	SBrushCommon::VertexFaceIndexType ptsize = (SBrushCommon::VertexFaceIndexType)pointindexlist.size();
	center = Vec3(0,0,0);

	for( int i = 0; i < ptsize; i++ )
	{
		center += vtxlist[pointindexlist[i]]->pos;
	}

	center /= ptsize;
}


void SBrushFace::MapEdgeIndexToPolyIndices( const std::vector<SBrushVertex*>& vtxlist,  
																						const std::vector<SBrushTriangle*>& trilist, 
																						const int& nEdgeIndex, 
																						int &rnFirstPolyVertex, 
																						int& rnSecondPolyVertex ) const
{
	int triindex	= nEdgeIndex / 3;
	int triedge		= nEdgeIndex % 3;
	rnFirstPolyVertex	= FindLocalVtxIndex(trilist[triangleidxlist[triindex]]->vertexindices[triedge]);
	rnSecondPolyVertex	= FindLocalVtxIndex(trilist[triangleidxlist[triindex]]->vertexindices[(triedge+1)%3]);
}


SBrushCommon::VertexFaceIndexType SBrushFace::FindLocalVtxIndex( SBrushCommon::VertexFaceIndexType globalidx ) const
{
	int size = (int)pointindexlist.size();
	for( int i = 0; i < size; i++ )
	{
		if( pointindexlist[i] == globalidx )
			return i;
	}
	return SBrushCommon::VertexFaceMax;
}

void SBrushFace::FitTexture( const std::vector<SBrushVertex*>& vlist, float tileU, float tileV )
{
	Vec3   mins,maxs;
	int i;
	float width, height;
	float rot_width, rot_height;
	float cosv,sinv,ang;
	float min_t, min_s, max_t, max_s;
	float s,t;
	Vec3 vecs[2];
	Vec3   coords[4];
	SBrushCommon::STexInfo *td;

	mins=SetMaxBB();
	maxs=SetMinBB();

	td = &texinfo;
	CalcBound( vlist, mins, maxs );
	ang = td->rotate / 180 * gf_PI;
	sinv = sinf(ang);
	cosv = cosf(ang);

	plane.CalcTextureAxis(vecs[0], vecs[1]);

	min_s = mins | vecs[0];
	min_t = mins | vecs[1];
	max_s = maxs | vecs[0];
	max_t = maxs | vecs[1];

	width = max_s - min_s;
	height = max_t - min_t;

	coords[0][0] = min_s;
	coords[0][1] = min_t;
	coords[1][0] = max_s;
	coords[1][1] = min_t;
	coords[2][0] = min_s;
	coords[2][1] = max_t;
	coords[3][0] = max_s;
	coords[3][1] = max_t;

	min_s = min_t = 99999;
	max_s = max_t = -99999;

	for (i=0; i<4; i++)
	{
		s = cosv * coords[i][0] - sinv * coords[i][1];
		t = sinv * coords[i][0] + cosv * coords[i][1];
		if (i&1)
		{
			if (s > max_s) 
				max_s = s;
		}
		else
		{
			if (s < min_s) 
				min_s = s;

			if (i<2)
			{
				if (t < min_t) 
					min_t = t;
			}
			else
			{
				if (t > max_t) 
					max_t = t;
			}
		}
	}

	rot_width =  (max_s - min_s);
	rot_height = (max_t - min_t);

	td->scale[0] = -rot_width/tileU;
	td->scale[1] = -rot_height/tileV;

	td->shift[0] = min_s/td->scale[0];
	td->shift[1] = min_t/td->scale[1];
}

void	SBrushFace::Invert( const std::vector<SBrushTriangle*>& triangleArray )
{
	plane.Invert();
	for( int i = 0, nTriSize(triangleidxlist.size()); i < nTriSize; ++i )
	{
		assert( triangleidxlist[i] >= 0 && triangleidxlist[i] < triangleArray.size() );
		SBrushTriangle* pTriangle = triangleArray[triangleidxlist[i]];
		pTriangle->Invert();
	}
}

int SBrushVertex::GetMemorySize()
{
	return sizeof(*this);
}

int SBrushTriangle::GetMemorySize()
{
	return sizeof(*this);
}

void SBrushTriangle::Invert()
{
	normal = (-normal);
	std::swap( vertexindices[0], vertexindices[2] );
}

int SBrushFace::GetMemorySize()
{
	int size = sizeof(*this);
	size += (int)pointindexlist.size() * sizeof(SBrushCommon::VertexFaceIndexType);
	size += (int)triangleidxlist.size() * sizeof(SBrushCommon::VertexFaceIndexType);
	return size;
}

void SBrushFace::CalcPlane( const std::vector<SBrushVertex*>& vertexlist )
{
	std::vector<Vec3> positionlist;

	for( size_t i = 0; i < pointindexlist.size(); ++i )
		positionlist.push_back( vertexlist[pointindexlist[i]]->pos );

	SBrushCommon::VertexFaceIndexType triIndex[3];
	bool bSuccess = SBrush::FindTrianglePointsInPolygonForFace( positionlist, triIndex[0], triIndex[1], triIndex[2] );
	plane = SBrushPlane(	vertexlist[triIndex[0]]->pos,
												vertexlist[triIndex[1]]->pos,
												vertexlist[triIndex[2]]->pos );
}